// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_
#define V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_

#include "src/ast/ast.h"
#include "src/base/compiler-specific.h"
#include "src/globals.h"
#include "src/interpreter/bytecode-array-writer.h"
#include "src/interpreter/bytecode-flags.h"
#include "src/interpreter/bytecode-register-allocator.h"
#include "src/interpreter/bytecode-register.h"
#include "src/interpreter/bytecode-source-info.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/constant-array-builder.h"
#include "src/interpreter/handler-table-builder.h"
#include "src/zone/zone-containers.h"

namespace v8 {
namespace internal {

    class FeedbackVectorSpec;
    class Isolate;

    namespace interpreter {

        class BytecodeLabel;
        class BytecodeLoopHeader;
        class BytecodeNode;
        class BytecodeRegisterOptimizer;
        class BytecodeJumpTable;
        class Register;

        class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
        public:
            BytecodeArrayBuilder(
                Zone* zone, int parameter_count, int locals_count,
                FeedbackVectorSpec* feedback_vector_spec = nullptr,
                SourcePositionTableBuilder::RecordingMode source_position_mode = SourcePositionTableBuilder::RECORD_SOURCE_POSITIONS);

            Handle<BytecodeArray> ToBytecodeArray(Isolate* isolate);

            // Get the number of parameters expected by function.
            int parameter_count() const
            {
                DCHECK_GE(parameter_count_, 0);
                return parameter_count_;
            }

            // Get the number of locals required for bytecode array.
            int locals_count() const
            {
                DCHECK_GE(local_register_count_, 0);
                return local_register_count_;
            }

            // Returns the number of fixed (non-temporary) registers.
            int fixed_register_count() const { return locals_count(); }

            // Returns the number of fixed and temporary registers.
            int total_register_count() const
            {
                DCHECK_LE(fixed_register_count(),
                    register_allocator()->maximum_register_count());
                return register_allocator()->maximum_register_count();
            }

            Register Local(int index) const;
            Register Parameter(int parameter_index) const;
            Register Receiver() const;

            // Constant loads to accumulator.
            BytecodeArrayBuilder& LoadConstantPoolEntry(size_t entry);
            BytecodeArrayBuilder& LoadLiteral(Smi value);
            BytecodeArrayBuilder& LoadLiteral(double value);
            BytecodeArrayBuilder& LoadLiteral(const AstRawString* raw_string);
            BytecodeArrayBuilder& LoadLiteral(const Scope* scope);
            BytecodeArrayBuilder& LoadLiteral(AstBigInt bigint);
            BytecodeArrayBuilder& LoadLiteral(AstSymbol symbol);
            BytecodeArrayBuilder& LoadUndefined();
            BytecodeArrayBuilder& LoadNull();
            BytecodeArrayBuilder& LoadTheHole();
            BytecodeArrayBuilder& LoadTrue();
            BytecodeArrayBuilder& LoadFalse();
            BytecodeArrayBuilder& LoadBoolean(bool value);

            // Global loads to the accumulator and stores from the accumulator.
            BytecodeArrayBuilder& LoadGlobal(const AstRawString* name, int feedback_slot,
                TypeofMode typeof_mode);
            BytecodeArrayBuilder& StoreGlobal(const AstRawString* name,
                int feedback_slot);

            // Load the object at |slot_index| at |depth| in the context chain starting
            // with |context| into the accumulator.
            enum ContextSlotMutability { kImmutableSlot,
                kMutableSlot };
            BytecodeArrayBuilder& LoadContextSlot(Register context, int slot_index,
                int depth,
                ContextSlotMutability immutable);

            // Stores the object in the accumulator into |slot_index| at |depth| in the
            // context chain starting with |context|.
            BytecodeArrayBuilder& StoreContextSlot(Register context, int slot_index,
                int depth);

            // Load from a module variable into the accumulator. |depth| is the depth of
            // the current context relative to the module context.
            BytecodeArrayBuilder& LoadModuleVariable(int cell_index, int depth);

            // Store from the accumulator into a module variable. |depth| is the depth of
            // the current context relative to the module context.
            BytecodeArrayBuilder& StoreModuleVariable(int cell_index, int depth);

            // Register-accumulator transfers.
            BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg);
            BytecodeArrayBuilder& StoreAccumulatorInRegister(Register reg);

            // Register-register transfer.
            BytecodeArrayBuilder& MoveRegister(Register from, Register to);

            // Named load property.
            BytecodeArrayBuilder& LoadNamedProperty(Register object,
                const AstRawString* name,
                int feedback_slot);
            // Named load property without feedback
            BytecodeArrayBuilder& LoadNamedPropertyNoFeedback(Register object,
                const AstRawString* name);

            // Keyed load property. The key should be in the accumulator.
            BytecodeArrayBuilder& LoadKeyedProperty(Register object, int feedback_slot);
            // Named load property of the @@iterator symbol.
            BytecodeArrayBuilder& LoadIteratorProperty(Register object,
                int feedback_slot);
            // Named load property of the @@asyncIterator symbol.
            BytecodeArrayBuilder& LoadAsyncIteratorProperty(Register object,
                int feedback_slot);

            // Store properties. Flag for NeedsSetFunctionName() should
            // be in the accumulator.
            BytecodeArrayBuilder& StoreDataPropertyInLiteral(
                Register object, Register name, DataPropertyInLiteralFlags flags,
                int feedback_slot);

            // Collect type information for developer tools. The value for which we
            // record the type is stored in the accumulator.
            BytecodeArrayBuilder& CollectTypeProfile(int position);

            // Store a property named by a property name. The value to be stored should be
            // in the accumulator.
            BytecodeArrayBuilder& StoreNamedProperty(Register object,
                const AstRawString* name,
                int feedback_slot,
                LanguageMode language_mode);

            // Store a property named by a property name without feedback slot. The value
            // to be stored should be in the accumulator.
            BytecodeArrayBuilder& StoreNamedPropertyNoFeedback(
                Register object, const AstRawString* name, LanguageMode language_mode);

            // Store a property named by a constant from the constant pool. The value to
            // be stored should be in the accumulator.
            BytecodeArrayBuilder& StoreNamedProperty(Register object,
                size_t constant_pool_entry,
                int feedback_slot,
                LanguageMode language_mode);
            // Store an own property named by a constant from the constant pool. The
            // value to be stored should be in the accumulator.
            BytecodeArrayBuilder& StoreNamedOwnProperty(Register object,
                const AstRawString* name,
                int feedback_slot);
            // Store a property keyed by a value in a register. The value to be stored
            // should be in the accumulator.
            BytecodeArrayBuilder& StoreKeyedProperty(Register object, Register key,
                int feedback_slot,
                LanguageMode language_mode);
            // Store an own element in an array literal. The value to be stored should be
            // in the accumulator.
            BytecodeArrayBuilder& StoreInArrayLiteral(Register array, Register index,
                int feedback_slot);
            // Store the home object property. The value to be stored should be in the
            // accumulator.
            BytecodeArrayBuilder& StoreHomeObjectProperty(Register object,
                int feedback_slot,
                LanguageMode language_mode);

            // Store the class fields property. The initializer to be stored should
            // be in the accumulator.
            BytecodeArrayBuilder& StoreClassFieldsInitializer(Register constructor,
                int feedback_slot);

            // Load class fields property.
            BytecodeArrayBuilder& LoadClassFieldsInitializer(Register constructor,
                int feedback_slot);

            // Lookup the variable with |name|.
            BytecodeArrayBuilder& LoadLookupSlot(const AstRawString* name,
                TypeofMode typeof_mode);

            // Lookup the variable with |name|, which is known to be at |slot_index| at
            // |depth| in the context chain if not shadowed by a context extension
            // somewhere in that context chain.
            BytecodeArrayBuilder& LoadLookupContextSlot(const AstRawString* name,
                TypeofMode typeof_mode,
                int slot_index, int depth);

            // Lookup the variable with |name|, which has its feedback in |feedback_slot|
            // and is known to be global if not shadowed by a context extension somewhere
            // up to |depth| in that context chain.
            BytecodeArrayBuilder& LoadLookupGlobalSlot(const AstRawString* name,
                TypeofMode typeof_mode,
                int feedback_slot, int depth);

            // Store value in the accumulator into the variable with |name|.
            BytecodeArrayBuilder& StoreLookupSlot(
                const AstRawString* name, LanguageMode language_mode,
                LookupHoistingMode lookup_hoisting_mode);

            // Create a new closure for a SharedFunctionInfo which will be inserted at
            // constant pool index |shared_function_info_entry|.
            BytecodeArrayBuilder& CreateClosure(size_t shared_function_info_entry,
                int slot, int flags);

            // Create a new local context for a |scope|.
            BytecodeArrayBuilder& CreateBlockContext(const Scope* scope);

            // Create a new context for a catch block with |exception| and |scope|.
            BytecodeArrayBuilder& CreateCatchContext(Register exception,
                const Scope* scope);

            // Create a new context with the given |scope| and size |slots|.
            BytecodeArrayBuilder& CreateFunctionContext(const Scope* scope, int slots);

            // Create a new eval context with the given |scope| and size |slots|.
            BytecodeArrayBuilder& CreateEvalContext(const Scope* scope, int slots);

            // Creates a new context with the given |scope| for a with-statement
            // with the |object| in a register.
            BytecodeArrayBuilder& CreateWithContext(Register object, const Scope* scope);

            // Create a new arguments object in the accumulator.
            BytecodeArrayBuilder& CreateArguments(CreateArgumentsType type);

            // Literals creation.  Constant elements should be in the accumulator.
            BytecodeArrayBuilder& CreateRegExpLiteral(const AstRawString* pattern,
                int literal_index, int flags);
            BytecodeArrayBuilder& CreateArrayLiteral(size_t constant_elements_entry,
                int literal_index, int flags);
            BytecodeArrayBuilder& CreateEmptyArrayLiteral(int literal_index);
            BytecodeArrayBuilder& CreateArrayFromIterable();
            BytecodeArrayBuilder& CreateObjectLiteral(size_t constant_properties_entry,
                int literal_index, int flags);
            BytecodeArrayBuilder& CreateEmptyObjectLiteral();
            BytecodeArrayBuilder& CloneObject(Register source, int flags,
                int feedback_slot);

            // Gets or creates the template for a TemplateObjectDescription which will
            // be inserted at constant pool index |template_object_description_entry|.
            BytecodeArrayBuilder& GetTemplateObject(
                size_t template_object_description_entry, int feedback_slot);

            // Push the context in accumulator as the new context, and store in register
            // |context|.
            BytecodeArrayBuilder& PushContext(Register context);

            // Pop the current context and replace with |context|.
            BytecodeArrayBuilder& PopContext(Register context);

            // Call a JS function which is known to be a property of a JS object. The
            // JSFunction or Callable to be called should be in |callable|. The arguments
            // should be in |args|, with the receiver in |args[0]|. The call type of the
            // expression is in |call_type|. Type feedback is recorded in the
            // |feedback_slot| in the type feedback vector.
            BytecodeArrayBuilder& CallProperty(Register callable, RegisterList args,
                int feedback_slot);

            // Call a JS function with an known undefined receiver. The JSFunction or
            // Callable to be called should be in |callable|. The arguments should be in
            // |args|, with no receiver as it is implicitly set to undefined. Type
            // feedback is recorded in the |feedback_slot| in the type feedback vector.
            BytecodeArrayBuilder& CallUndefinedReceiver(Register callable,
                RegisterList args,
                int feedback_slot);

            // Call a JS function with an any receiver, possibly (but not necessarily)
            // undefined. The JSFunction or Callable to be called should be in |callable|.
            // The arguments should be in |args|, with the receiver in |args[0]|. Type
            // feedback is recorded in the |feedback_slot| in the type feedback vector.
            BytecodeArrayBuilder& CallAnyReceiver(Register callable, RegisterList args,
                int feedback_slot);

            // Call a JS function with an any receiver, possibly (but not necessarily)
            // undefined. The JSFunction or Callable to be called should be in |callable|.
            // The arguments should be in |args|, with the receiver in |args[0]|.
            BytecodeArrayBuilder& CallNoFeedback(Register callable, RegisterList args);

            // Tail call into a JS function. The JSFunction or Callable to be called
            // should be in |callable|. The arguments should be in |args|, with the
            // receiver in |args[0]|. Type feedback is recorded in the |feedback_slot| in
            // the type feedback vector.
            BytecodeArrayBuilder& TailCall(Register callable, RegisterList args,
                int feedback_slot);

            // Call a JS function. The JSFunction or Callable to be called should be in
            // |callable|, the receiver in |args[0]| and the arguments in |args[1]|
            // onwards. The final argument must be a spread.
            BytecodeArrayBuilder& CallWithSpread(Register callable, RegisterList args,
                int feedback_slot);

            // Call the Construct operator. The accumulator holds the |new_target|.
            // The |constructor| is in a register and arguments are in |args|.
            BytecodeArrayBuilder& Construct(Register constructor, RegisterList args,
                int feedback_slot);

            // Call the Construct operator for use with a spread. The accumulator holds
            // the |new_target|. The |constructor| is in a register and arguments are in
            // |args|. The final argument must be a spread.
            BytecodeArrayBuilder& ConstructWithSpread(Register constructor,
                RegisterList args,
                int feedback_slot);

            // Call the runtime function with |function_id| and arguments |args|.
            BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
                RegisterList args);
            // Call the runtime function with |function_id| with single argument |arg|.
            BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
                Register arg);
            // Call the runtime function with |function_id| with no arguments.
            BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id);

            // Call the runtime function with |function_id| and arguments |args|, that
            // returns a pair of values. The return values will be returned in
            // |return_pair|.
            BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id,
                RegisterList args,
                RegisterList return_pair);
            // Call the runtime function with |function_id| with single argument |arg|
            // that returns a pair of values. The return values will be returned in
            // |return_pair|.
            BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id,
                Register arg,
                RegisterList return_pair);

            // Call the JS runtime function with |context_index| and arguments |args|,
            // with no receiver as it is implicitly set to undefined.
            BytecodeArrayBuilder& CallJSRuntime(int context_index, RegisterList args);

            // Operators (register holds the lhs value, accumulator holds the rhs value).
            // Type feedback will be recorded in the |feedback_slot|
            BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg,
                int feedback_slot);
            // Same as above, but lhs in the accumulator and rhs in |literal|.
            BytecodeArrayBuilder& BinaryOperationSmiLiteral(Token::Value binop,
                Smi literal,
                int feedback_slot);

            // Unary and Count Operators (value stored in accumulator).
            // Type feedback will be recorded in the |feedback_slot|
            BytecodeArrayBuilder& UnaryOperation(Token::Value op, int feedback_slot);

            enum class ToBooleanMode {
                kConvertToBoolean, // Perform ToBoolean conversion on accumulator.
                kAlreadyBoolean, // Accumulator is already a Boolean.
            };

            // Unary Operators.
            BytecodeArrayBuilder& LogicalNot(ToBooleanMode mode);
            BytecodeArrayBuilder& TypeOf();

            // Expects a heap object in the accumulator. Returns its super constructor in
            // the register |out| if it passes the IsConstructor test. Otherwise, it
            // throws a TypeError exception.
            BytecodeArrayBuilder& GetSuperConstructor(Register out);

            // Deletes property from an object. This expects that accumulator contains
            // the key to be deleted and the register contains a reference to the object.
            BytecodeArrayBuilder& Delete(Register object, LanguageMode language_mode);

            // JavaScript defines two kinds of 'nil'.
            enum NilValue { kNullValue,
                kUndefinedValue };

            // Tests.
            BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg,
                int feedback_slot);
            BytecodeArrayBuilder& CompareReference(Register reg);
            BytecodeArrayBuilder& CompareUndetectable();
            BytecodeArrayBuilder& CompareUndefined();
            BytecodeArrayBuilder& CompareNull();
            BytecodeArrayBuilder& CompareNil(Token::Value op, NilValue nil);
            BytecodeArrayBuilder& CompareTypeOf(
                TestTypeOfFlags::LiteralFlag literal_flag);

            // Converts accumulator and stores result in register |out|.
            BytecodeArrayBuilder& ToObject(Register out);
            BytecodeArrayBuilder& ToName(Register out);
            BytecodeArrayBuilder& ToString();

            // Converts accumulator and stores result back in accumulator.
            BytecodeArrayBuilder& ToNumber(int feedback_slot);
            BytecodeArrayBuilder& ToNumeric(int feedback_slot);

            // Exception handling.
            BytecodeArrayBuilder& MarkHandler(int handler_id,
                HandlerTable::CatchPrediction will_catch);
            BytecodeArrayBuilder& MarkTryBegin(int handler_id, Register context);
            BytecodeArrayBuilder& MarkTryEnd(int handler_id);

            // Flow Control.
            BytecodeArrayBuilder& Bind(BytecodeLabel* label);
            BytecodeArrayBuilder& Bind(BytecodeLoopHeader* label);
            BytecodeArrayBuilder& Bind(BytecodeJumpTable* jump_table, int case_value);

            BytecodeArrayBuilder& Jump(BytecodeLabel* label);
            BytecodeArrayBuilder& JumpLoop(BytecodeLoopHeader* loop_header,
                int loop_depth);

            BytecodeArrayBuilder& JumpIfTrue(ToBooleanMode mode, BytecodeLabel* label);
            BytecodeArrayBuilder& JumpIfFalse(ToBooleanMode mode, BytecodeLabel* label);
            BytecodeArrayBuilder& JumpIfJSReceiver(BytecodeLabel* label);
            BytecodeArrayBuilder& JumpIfNull(BytecodeLabel* label);
            BytecodeArrayBuilder& JumpIfNotNull(BytecodeLabel* label);
            BytecodeArrayBuilder& JumpIfUndefined(BytecodeLabel* label);
            BytecodeArrayBuilder& JumpIfNotUndefined(BytecodeLabel* label);
            BytecodeArrayBuilder& JumpIfNil(BytecodeLabel* label, Token::Value op,
                NilValue nil);
            BytecodeArrayBuilder& JumpIfNotNil(BytecodeLabel* label, Token::Value op,
                NilValue nil);

            BytecodeArrayBuilder& SwitchOnSmiNoFeedback(BytecodeJumpTable* jump_table);

            BytecodeArrayBuilder& StackCheck(int position);

            // Sets the pending message to the value in the accumulator, and returns the
            // previous pending message in the accumulator.
            BytecodeArrayBuilder& SetPendingMessage();

            BytecodeArrayBuilder& Throw();
            BytecodeArrayBuilder& ReThrow();
            BytecodeArrayBuilder& Abort(AbortReason reason);
            BytecodeArrayBuilder& Return();
            BytecodeArrayBuilder& ThrowReferenceErrorIfHole(const AstRawString* name);
            BytecodeArrayBuilder& ThrowSuperNotCalledIfHole();
            BytecodeArrayBuilder& ThrowSuperAlreadyCalledIfNotHole();

            // Debugger.
            BytecodeArrayBuilder& Debugger();

            // Increment the block counter at the given slot (block code coverage).
            BytecodeArrayBuilder& IncBlockCounter(int slot);

            // Complex flow control.
            BytecodeArrayBuilder& ForInEnumerate(Register receiver);
            BytecodeArrayBuilder& ForInPrepare(RegisterList cache_info_triple,
                int feedback_slot);
            BytecodeArrayBuilder& ForInContinue(Register index, Register cache_length);
            BytecodeArrayBuilder& ForInNext(Register receiver, Register index,
                RegisterList cache_type_array_pair,
                int feedback_slot);
            BytecodeArrayBuilder& ForInStep(Register index);

            // Generators.
            BytecodeArrayBuilder& SuspendGenerator(Register generator,
                RegisterList registers,
                int suspend_id);
            BytecodeArrayBuilder& SwitchOnGeneratorState(Register generator,
                BytecodeJumpTable* jump_table);
            BytecodeArrayBuilder& ResumeGenerator(Register generator,
                RegisterList registers);

            // Creates a new handler table entry and returns a {hander_id} identifying the
            // entry, so that it can be referenced by above exception handling support.
            int NewHandlerEntry() { return handler_table_builder()->NewHandlerEntry(); }

            // Allocates a new jump table of given |size| and |case_value_base| in the
            // constant pool.
            BytecodeJumpTable* AllocateJumpTable(int size, int case_value_base);

            // Gets a constant pool entry.
            size_t GetConstantPoolEntry(const AstRawString* raw_string);
            size_t GetConstantPoolEntry(AstBigInt bigint);
            size_t GetConstantPoolEntry(const Scope* scope);
            size_t GetConstantPoolEntry(double number);
#define ENTRY_GETTER(NAME, ...) size_t NAME##ConstantPoolEntry();
            SINGLETON_CONSTANT_ENTRY_TYPES(ENTRY_GETTER)
#undef ENTRY_GETTER

            // Allocates a slot in the constant pool which can later be set.
            size_t AllocateDeferredConstantPoolEntry();
            // Sets the deferred value into an allocated constant pool entry.
            void SetDeferredConstantPoolEntry(size_t entry, Handle<Object> object);

            void InitializeReturnPosition(FunctionLiteral* literal);

            void SetStatementPosition(Statement* stmt)
            {
                if (stmt->position() == kNoSourcePosition)
                    return;
                latest_source_info_.MakeStatementPosition(stmt->position());
            }

            void SetExpressionPosition(Expression* expr)
            {
                SetExpressionPosition(expr->position());
            }

            void SetExpressionPosition(int position)
            {
                if (position == kNoSourcePosition)
                    return;
                if (!latest_source_info_.is_statement()) {
                    // Ensure the current expression position is overwritten with the
                    // latest value.
                    latest_source_info_.MakeExpressionPosition(position);
                }
            }

            void SetExpressionAsStatementPosition(Expression* expr)
            {
                if (expr->position() == kNoSourcePosition)
                    return;
                latest_source_info_.MakeStatementPosition(expr->position());
            }

            void SetReturnPosition(int source_position, FunctionLiteral* literal)
            {
                if (source_position != kNoSourcePosition) {
                    latest_source_info_.MakeStatementPosition(source_position);
                } else if (literal->return_position() != kNoSourcePosition) {
                    latest_source_info_.MakeStatementPosition(literal->return_position());
                }
            }

            bool RemainderOfBlockIsDead() const
            {
                return bytecode_array_writer_.RemainderOfBlockIsDead();
            }

            // Returns the raw operand value for the given register or register list.
            uint32_t GetInputRegisterOperand(Register reg);
            uint32_t GetOutputRegisterOperand(Register reg);
            uint32_t GetInputRegisterListOperand(RegisterList reg_list);
            uint32_t GetOutputRegisterListOperand(RegisterList reg_list);

            // Outputs raw register transfer bytecodes without going through the register
            // optimizer.
            void OutputLdarRaw(Register reg);
            void OutputStarRaw(Register reg);
            void OutputMovRaw(Register src, Register dest);

            // Accessors
            BytecodeRegisterAllocator* register_allocator()
            {
                return &register_allocator_;
            }
            const BytecodeRegisterAllocator* register_allocator() const
            {
                return &register_allocator_;
            }
            Zone* zone() const { return zone_; }

        private:
            friend class BytecodeRegisterAllocator;
            template <Bytecode bytecode, AccumulatorUse accumulator_use,
                OperandType... operand_types>
            friend class BytecodeNodeBuilder;

            const FeedbackVectorSpec* feedback_vector_spec() const
            {
                return feedback_vector_spec_;
            }

            // Returns the current source position for the given |bytecode|.
            V8_INLINE BytecodeSourceInfo CurrentSourcePosition(Bytecode bytecode);

#define DECLARE_BYTECODE_OUTPUT(Name, ...)                           \
    template <typename... Operands>                                  \
    V8_INLINE BytecodeNode Create##Name##Node(Operands... operands); \
    template <typename... Operands>                                  \
    V8_INLINE void Output##Name(Operands... operands);               \
    template <typename... Operands>                                  \
    V8_INLINE void Output##Name(BytecodeLabel* label, Operands... operands);
            BYTECODE_LIST(DECLARE_BYTECODE_OUTPUT)
#undef DECLARE_OPERAND_TYPE_INFO

            V8_INLINE void OutputJumpLoop(BytecodeLoopHeader* loop_header,
                int loop_depth);
            V8_INLINE void OutputSwitchOnSmiNoFeedback(BytecodeJumpTable* jump_table);

            bool RegisterIsValid(Register reg) const;
            bool RegisterListIsValid(RegisterList reg_list) const;

            // Sets a deferred source info which should be emitted before any future
            // source info (either attached to a following bytecode or as a nop).
            void SetDeferredSourceInfo(BytecodeSourceInfo source_info);
            // Either attach deferred source info to node, or emit it as a nop bytecode
            // if node already have valid source info.
            void AttachOrEmitDeferredSourceInfo(BytecodeNode* node);

            // Write bytecode to bytecode array.
            void Write(BytecodeNode* node);
            void WriteJump(BytecodeNode* node, BytecodeLabel* label);
            void WriteJumpLoop(BytecodeNode* node, BytecodeLoopHeader* loop_header);
            void WriteSwitch(BytecodeNode* node, BytecodeJumpTable* label);

            // Not implemented as the illegal bytecode is used inside internally
            // to indicate a bytecode field is not valid or an error has occurred
            // during bytecode generation.
            BytecodeArrayBuilder& Illegal();

            template <Bytecode bytecode, AccumulatorUse accumulator_use>
            void PrepareToOutputBytecode();

            BytecodeArrayWriter* bytecode_array_writer()
            {
                return &bytecode_array_writer_;
            }
            ConstantArrayBuilder* constant_array_builder()
            {
                return &constant_array_builder_;
            }
            const ConstantArrayBuilder* constant_array_builder() const
            {
                return &constant_array_builder_;
            }
            HandlerTableBuilder* handler_table_builder()
            {
                return &handler_table_builder_;
            }

            Zone* zone_;
            FeedbackVectorSpec* feedback_vector_spec_;
            bool bytecode_generated_;
            ConstantArrayBuilder constant_array_builder_;
            HandlerTableBuilder handler_table_builder_;
            int parameter_count_;
            int local_register_count_;
            BytecodeRegisterAllocator register_allocator_;
            BytecodeArrayWriter bytecode_array_writer_;
            BytecodeRegisterOptimizer* register_optimizer_;
            BytecodeSourceInfo latest_source_info_;
            BytecodeSourceInfo deferred_source_info_;

            DISALLOW_COPY_AND_ASSIGN(BytecodeArrayBuilder);
        };

        V8_EXPORT_PRIVATE std::ostream& operator<<(
            std::ostream& os, const BytecodeArrayBuilder::ToBooleanMode& mode);

    } // namespace interpreter
} // namespace internal
} // namespace v8

#endif // V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_
